[[mvc-cors]]
= CORS
[.small]#xref:web/webflux-cors.adoc[See equivalent in the Reactive stack]#

Spring MVC lets you handle CORS (Cross-Origin Resource Sharing). This section
describes how to do so.




[[mvc-cors-intro]]
== Introduction
[.small]#xref:web/webflux-cors.adoc#webflux-cors-intro[See equivalent in the Reactive stack]#

For security reasons, browsers prohibit AJAX calls to resources outside the current origin.
For example, you could have your bank account in one tab and evil.com in another. Scripts
from evil.com should not be able to make AJAX requests to your bank API with your
credentials -- for example withdrawing money from your account!

Cross-Origin Resource Sharing (CORS) is a https://www.w3.org/TR/cors/[W3C specification]
implemented by https://caniuse.com/#feat=cors[most browsers] that lets you specify
what kind of cross-domain requests are authorized, rather than using less secure and less
powerful workarounds based on IFRAME or JSONP.




[[mvc-cors-credentialed-requests]]
== Credentialed Requests
[.small]#xref:web/webflux-cors.adoc#webflux-cors-credentialed-requests[See equivalent in the Reactive stack]#

Using CORS with credentialed requests requires enabling `allowedCredentials`. Be aware that
this option establishes a high level of trust with the configured domains and also increases
the surface of attack of the web application by exposing sensitive user-specific information
such as cookies and CSRF tokens.

Enabling credentials also impacts how the configured `"*"` CORS wildcards are processed:

* Wildcards are not authorized in `allowOrigins`, but alternatively
the `allowOriginPatterns` property may be used to match to a dynamic set of origins.
* When set on `allowedHeaders` or `allowedMethods`, the `Access-Control-Allow-Headers`
and `Access-Control-Allow-Methods` response headers are handled by copying the related
headers and method specified in the CORS preflight request.
* When set on `exposedHeaders`, `Access-Control-Expose-Headers` response header is set
either to the configured list of headers or to the wildcard character. While the CORS spec
does not allow the wildcard character when `Access-Control-Allow-Credentials` is set to
`true`, most browsers support it and the response headers are not all available during the
CORS processing, so as a consequence the wildcard character is the header value used when
specified regardless of the value of the `allowCredentials` property.

WARNING: While such wildcard configuration can be handy, it is recommended when possible to configure
a finite set of values instead to provide a higher level of security.




[[mvc-cors-processing]]
== Processing
[.small]#xref:web/webflux-cors.adoc#webflux-cors-processing[See equivalent in the Reactive stack]#

The CORS specification distinguishes between preflight, simple, and actual requests.
To learn how CORS works, you can read
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[this article], among
many others, or see the specification for more details.

Spring MVC `HandlerMapping` implementations provide built-in support for CORS. After successfully
mapping a request to a handler, `HandlerMapping` implementations check the CORS configuration for the
given request and handler and take further actions. Preflight requests are handled
directly, while simple and actual CORS requests are intercepted, validated, and have
required CORS response headers set.

In order to enable cross-origin requests (that is, the `Origin` header is present and
differs from the host of the request), you need to have some explicitly declared CORS
configuration. If no matching CORS configuration is found, preflight requests are
rejected. No CORS headers are added to the responses of simple and actual CORS requests
and, consequently, browsers reject them.

Each `HandlerMapping` can be
{spring-framework-api}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
individually with URL pattern-based `CorsConfiguration` mappings. In most cases, applications
use the MVC Java configuration or the XML namespace to declare such mappings, which results
in a single global map being passed to all `HandlerMapping` instances.

You can combine global CORS configuration at the `HandlerMapping` level with more
fine-grained, handler-level CORS configuration. For example, annotated controllers can use
class- or method-level `@CrossOrigin` annotations (other handlers can implement
`CorsConfigurationSource`).

The rules for combining global and local configuration are generally additive -- for example,
all global and all local origins. For those attributes where only a single value can be
accepted, e.g. `allowCredentials` and `maxAge`, the local overrides the global value. See
{spring-framework-api}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`]
for more details.

[TIP]
====
To learn more from the source or make advanced customizations, check the code behind:

* `CorsConfiguration`
* `CorsProcessor`, `DefaultCorsProcessor`
* `AbstractHandlerMapping`
====




[[mvc-cors-controller]]
== `@CrossOrigin`
[.small]#xref:web/webflux-cors.adoc#webflux-cors-controller[See equivalent in the Reactive stack]#

The {spring-framework-api}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation enables cross-origin requests on annotated controller methods,
as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	@RestController
	@RequestMapping("/account")
	public class AccountController {

		@CrossOrigin
		@GetMapping("/{id}")
		public Account retrieve(@PathVariable Long id) {
			// ...
		}

		@DeleteMapping("/{id}")
		public void remove(@PathVariable Long id) {
			// ...
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	@RestController
	@RequestMapping("/account")
	class AccountController {

		@CrossOrigin
		@GetMapping("/{id}")
		fun retrieve(@PathVariable id: Long): Account {
			// ...
		}

		@DeleteMapping("/{id}")
		fun remove(@PathVariable id: Long) {
			// ...
		}
	}
----
======

By default, `@CrossOrigin` allows:

* All origins.
* All headers.
* All HTTP methods to which the controller method is mapped.

`allowCredentials` is not enabled by default, since that establishes a trust level
that exposes sensitive user-specific information (such as cookies and CSRF tokens) and
should only be used where appropriate. When it is enabled either `allowOrigins` must be
set to one or more specific domain (but not the special value `"*"`) or alternatively
the `allowOriginPatterns` property may be used to match to a dynamic set of origins.

`maxAge` is set to 30 minutes.

`@CrossOrigin` is supported at the class level, too, and is inherited by all methods,
as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

	@GetMapping("/{id}")
	public Account retrieve(@PathVariable Long id) {
		// ...
	}

	@DeleteMapping("/{id}")
	public void remove(@PathVariable Long id) {
		// ...
	}
}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	@CrossOrigin(origins = ["https://domain2.com"], maxAge = 3600)
	@RestController
	@RequestMapping("/account")
	class AccountController {

		@GetMapping("/{id}")
		fun retrieve(@PathVariable id: Long): Account {
			// ...
		}

		@DeleteMapping("/{id}")
		fun remove(@PathVariable id: Long) {
			// ...
		}
----
======

You can use `@CrossOrigin` at both the class level and the method level,
as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	@CrossOrigin(maxAge = 3600)
	@RestController
	@RequestMapping("/account")
	public class AccountController {

		@CrossOrigin("https://domain2.com")
		@GetMapping("/{id}")
		public Account retrieve(@PathVariable Long id) {
			// ...
		}

		@DeleteMapping("/{id}")
		public void remove(@PathVariable Long id) {
			// ...
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	@CrossOrigin(maxAge = 3600)
	@RestController
	@RequestMapping("/account")
	class AccountController {

		@CrossOrigin("https://domain2.com")
		@GetMapping("/{id}")
		fun retrieve(@PathVariable id: Long): Account {
			// ...
		}

		@DeleteMapping("/{id}")
		fun remove(@PathVariable id: Long) {
			// ...
		}
	}
----
======




[[mvc-cors-global]]
== Global Configuration
[.small]#xref:web/webflux-cors.adoc#webflux-cors-global[See equivalent in the Reactive stack]#

In addition to fine-grained, controller method level configuration, you probably want to
define some global CORS configuration, too. You can set URL-based `CorsConfiguration`
mappings individually on any `HandlerMapping`. Most applications, however, use the
MVC Java configuration or the MVC XML namespace to do that.

By default, global configuration enables the following:

* All origins.
* All headers.
* `GET`, `HEAD`, and `POST` methods.


`allowCredentials` is not enabled by default, since that establishes a trust level
that exposes sensitive user-specific information (such as cookies and CSRF tokens) and
should only be used where appropriate. When it is enabled either `allowOrigins` must be
set to one or more specific domain (but not the special value `"*"`) or alternatively
the `allowOriginPatterns` property may be used to match to a dynamic set of origins.

`maxAge` is set to 30 minutes.



[[mvc-cors-global-java]]
=== Java Configuration
[.small]#xref:web/webflux-cors.adoc#webflux-cors-global[See equivalent in the Reactive stack]#

To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback,
as the following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
	@Configuration
	@EnableWebMvc
	public class WebConfig implements WebMvcConfigurer {

		@Override
		public void addCorsMappings(CorsRegistry registry) {

			registry.addMapping("/api/**")
				.allowedOrigins("https://domain2.com")
				.allowedMethods("PUT", "DELETE")
				.allowedHeaders("header1", "header2", "header3")
				.exposedHeaders("header1", "header2")
				.allowCredentials(true).maxAge(3600);

			// Add more mappings...
		}
	}
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
	@Configuration
	@EnableWebMvc
	class WebConfig : WebMvcConfigurer {

		override fun addCorsMappings(registry: CorsRegistry) {

			registry.addMapping("/api/**")
					.allowedOrigins("https://domain2.com")
					.allowedMethods("PUT", "DELETE")
					.allowedHeaders("header1", "header2", "header3")
					.exposedHeaders("header1", "header2")
					.allowCredentials(true).maxAge(3600)

			// Add more mappings...
		}
	}
----
======



[[mvc-cors-global-xml]]
=== XML Configuration

To enable CORS in the XML namespace, you can use the `<mvc:cors>` element,
as the following example shows:

[source,xml,indent=0,subs="verbatim"]
----
<mvc:cors>

	<mvc:mapping path="/api/**"
		allowed-origins="https://domain1.com, https://domain2.com"
		allowed-methods="GET, PUT"
		allowed-headers="header1, header2, header3"
		exposed-headers="header1, header2" allow-credentials="true"
		max-age="123" />

	<mvc:mapping path="/resources/**"
		allowed-origins="https://domain1.com" />

</mvc:cors>
----




[[mvc-cors-filter]]
== CORS Filter
[.small]#xref:web/webflux-cors.adoc#webflux-cors-webfilter[See equivalent in the Reactive stack]#

You can apply CORS support through the built-in
{spring-framework-api}/web/filter/CorsFilter.html[`CorsFilter`].

NOTE: If you try to use the `CorsFilter` with Spring Security, keep in mind that Spring
Security has {docs-spring-security}/servlet/integrations/cors.html[built-in support] for
CORS.

To configure the filter, pass a `CorsConfigurationSource` to its constructor, as the
following example shows:

[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
	CorsConfiguration config = new CorsConfiguration();

	// Possibly...
	// config.applyPermitDefaultValues()

	config.setAllowCredentials(true);
	config.addAllowedOrigin("https://domain1.com");
	config.addAllowedHeader("*");
	config.addAllowedMethod("*");

	UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
	source.registerCorsConfiguration("/**", config);

	CorsFilter filter = new CorsFilter(source);
----

Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
	val config = CorsConfiguration()

	// Possibly...
	// config.applyPermitDefaultValues()

	config.allowCredentials = true
	config.addAllowedOrigin("https://domain1.com")
	config.addAllowedHeader("*")
	config.addAllowedMethod("*")

	val source = UrlBasedCorsConfigurationSource()
	source.registerCorsConfiguration("/**", config)

	val filter = CorsFilter(source)
----
======
